package main

import(
    "context"
    "crypto/tls"
    "crypto/x509"
    "encoding/json"
    "errors"
    "fmt"
    "io/ioutil"
    "math/rand"
    "path"
    "strings"
    "time"

    "etcd-manager-tls/conf"
    etcd "github.com/coreos/etcd/clientv3"
    "k8s.io/klog"
)

const prefix = "/appdns"

type EtcdClient struct {
     Client *etcd.Client
     Ctx    context.Context
}

type EtcdKeyValue struct {
     Host string `json:"host"`
     Port int    `json:"port"`
}

func init() {
     rand.Seed(time.Now().Unix())
}

/**
* @Description: 初始化etcd客户端连接
* @Author: liangan
* @Date: 2023-08-27_10:25
 */
func InitEtcd(etcdAddr []string, caCertFile, serverCertFile, serverKeyFile string) (*EtcdClient, error) {
     var (
         config etcd.Config
         client *etcd.Client
	 err    error
     )
    caCert, err := ioutil.ReadFile(caCertFile)
    if err != nil {
        klog.Errorf("%s, fail to load etcd ca cert, due to %s", caCertFile, err.Error())
        return nil, err
    }
    caCertPool := x509.NewCertPool()
    caCertPool.AppendCertsFromPEM(caCert)
    serverCert, err := tls.LoadX509KeyPair(serverCertFile, serverKeyFile)
    if err != nil {
        klog.Errorf("%s, fail to load etcd server cert, due to %s", caCertFile, err.Error())
        return nil, err
    }

     config = etcd.Config{
        Endpoints:   etcdAddr,
        DialTimeout: 30 * time.Second,
         TLS: &tls.Config{
             RootCAs:      caCertPool,
             Certificates: []tls.Certificate{serverCert},
         },
     }

     client, err = etcd.New(config)
     if err != nil {
        klog.Errorf("%s, fail to create etcd client, due to %s", etcdAddr, err.Error())
        return nil, errors.New("fail to connect to etcd address")
     }
     etcdClient := new(EtcdClient)
     etcdClient.Client = client
     etcdClient.Ctx = context.Background()
     return etcdClient, nil
}

/**
* @Description: etcd写数据
* @Author: liangan
* @Date: 2023-08-27_11:26
 */
func WriteRecord(etcdClient EtcdClient, domain, ip, domainPrefix string) error {
     etcdKey, err := parseDomainToEtcdKey(fmt.Sprintf("%s.%s", domainPrefix, domain))
     if err != nil {
        return err
     }
     kv := new(EtcdKeyValue)
     kv.Host = ip	//kv.Port = port
     data, _ := json.Marshal(kv)
     klog.Infof("create > domain:%s, etcd key:%s, etcd val:%s", domain, etcdKey, string(data))
     _, err = etcdClient.Client.Put(etcdClient.Ctx, etcdKey, string(data))

     return err
}

/**
* @Description: 查询数据
* @Author: liangan
* @Date: 2023-08-27_11:45
 */
func GetRecord(etcdClient EtcdClient, domain, domainPrefix string)  error {
    etcdKey, err := parseDomainToEtcdKey(fmt.Sprintf("%s.%s", domainPrefix, domain))
    klog.Infof("etcdKey:%s", etcdKey)
    if err != nil {
        return err
    }
    resp, err := etcdClient.Client.Get(etcdClient.Ctx, etcdKey)
    for _, ev := range resp.Kvs {
        klog.Infof("%s:%s\n", ev.Key, ev.Value)
    }

    return err
}

/**
* @Description: 删除数据
* @Author: liangan
* @Date: 2023-08-27_11:50
 */
func DelRecord(etcdClient EtcdClient, domain, domainPrefix string) error {
    klog.Infof("delete > domain:%s", domainPrefix+"."+domain)
    etcdKey, err := parseDomainToEtcdKey(fmt.Sprintf("%s.%s", domainPrefix, domain))
    if err != nil {
        return err
    }
    _, err = etcdClient.Client.Delete(etcdClient.Ctx, etcdKey)

    return err
}

/**
* @Description: 对有证书版etcd写、读、删数据操作
* @Author: liangan
* @Date: 2023-08-27_10:30
 */
func main() {
    //加载配置文件
    configMap := conf.InitConfig("conf/config.conf")

    //var etcdAddr = []string{"192.168.2.10:2379"}
    var etcdAddr = []string{configMap["etcd_addrs"]}
    var caCertFile = configMap["ca_cert_file"]
    var serverCertFile = configMap["server_cert_file"]
    var serverKeyFile = configMap["server_key_file"]
    if len(etcdAddr) ==0 || caCertFile =="" || serverCertFile== "" || serverKeyFile== ""{
        fmt.Println("The etcd parameter is null.")
        return
    }
    // 创建etcd client
    etcdClient, err := InitEtcd(etcdAddr, caCertFile, serverCertFile,serverKeyFile)
    //etcdClient, err := InitEtcd(etcdAddr, "./certs/ca.crt", "./certs/server.crt","./certs/server.key")
    if err != nil {
        klog.Errorf("%s, fail to create etcd client, due to %s ", etcdAddr, err.Error())
        return
    }
    var (
       domain = "9e68e4f9eb25.apps.paas.learn.com"
       ip = "172.16.16.122"
       domainPrefix = "k8s-biz"
    )

    // etcd写数据
    err = WriteRecord(*etcdClient, domain, ip, domainPrefix)
    if err != nil {
       klog.Warningf(" host:%s, ip:%s, failed to write to etcd, due to %s",domain, ip, err.Error())
    }

    // 查询一个key值
    err = GetRecord(*etcdClient, domain, domainPrefix)
    if err != nil {
        klog.Warningf(" host:%s, failed to get to etcd, due to %s",domain, err.Error())
    }

    // 删除一个key
    //err = DelRecord(*etcdClient, domain, domainPrefix)
    //if err != nil {
    //    klog.Warningf(" host:%s, failed to del to etcd, due to %s",domain, err.Error())
    //}
}

// 把域名格式转/格式
func parseDomainToEtcdKey(domain string) (string, error) {
     if !strings.Contains(domain, ".") {
        klog.Errorf("domain:%s, parse failed", domain)
        return "", errors.New("parse domain failed")
     }
     // 先把domain进行反转
     return path.Join(prefix, strings.Join(swapArrElement(strings.Split(domain, ".")), "/")), nil
}

func swapArrElement(arr []string) []string {
    for i := 0; i < len(arr); i++ {
        for j := i + 1; j < len(arr); j++ {
            temp := arr[i]
            arr[i] = arr[j]
            arr[j] = temp
        }
    }
    return arr
}

